package checkers;

import java.util.ArrayList;
import java.util.List;
import ai.Problem;

public class CheckersProblem implements Problem {

    private CheckersState currentState;

    public CheckersProblem() {
        initInitialState();
    }

    private void initInitialState() {
        currentState = new CheckersState(CheckersState.WHITE_PLAYER);

        byte[][] board = new byte[8][8];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 8; j++) {
                if (i % 2 == j % 2) {
                    board[i + 5][j] = CheckersState.WHITE_COMMON;
                } else {
                    board[i][j] = CheckersState.BLACK_COMMON;
                }
            }
        }
        
        //board[2][5] = CheckersState.WHITE_COMMON;
        //board[0][1] = CheckersState.WHITE_KING;
        //board[1][4] = CheckersState.BLACK_COMMON;        
        //board[1][2] = CheckersState.BLACK_COMMON;        
        //board[3][0] = CheckersState.BLACK_COMMON;        
        //board[7][6] = CheckersState.BLACK_KING;
        


        /*board[0][1] = CheckersState.BLACK_KING;
         board[1][0] = CheckersState.WHITE_KING;
         board[4][1] = CheckersState.WHITE_KING;
         board[6][1] = CheckersState.WHITE_KING;
         board[1][2]= CheckersState.BLACK_KING;
         board[7][0]= CheckersState.BLACK_KING;*/

        
         /*board[3][0] = CheckersState.BLACK_KING;
         board[1][0] = CheckersState.WHITE_KING;
         board[0][1] = CheckersState.BLANK_POSITION;
         board[0][5] = CheckersState.BLANK_POSITION;
         board[3][2] = CheckersState.BLACK_KING;
         board[2][1] = CheckersState.BLANK_POSITION;
         board[2][3] = CheckersState.BLANK_POSITION;
         board[2][7] = CheckersState.BLACK_KING;
         board[4][1] = CheckersState.WHITE_KING;
         board[5][0] = CheckersState.BLANK_POSITION;
         board[5][2] = CheckersState.BLANK_POSITION;
         board[7][0] = CheckersState.BLANK_POSITION;
         board[7][2] = CheckersState.BLANK_POSITION;
         board[7][4] = CheckersState.BLANK_POSITION;
         board[7][6] = CheckersState.BLANK_POSITION;
         board[5][6] = CheckersState.BLANK_POSITION;*/
         


        /*board[0][1] = CheckersState.BLANK_POSITION;
         board[0][3] = CheckersState.BLANK_POSITION;
         board[0][5] = CheckersState.BLANK_POSITION;
         board[0][7] = CheckersState.BLANK_POSITION;
         //board[1][6] = CheckersState.BLANK_POSITION;
         board[2][1] = CheckersState.BLANK_POSITION;
         board[2][3] = CheckersState.BLANK_POSITION;
         board[2][5] = CheckersState.BLANK_POSITION;
         board[2][7] = CheckersState.BLANK_POSITION;
         board[3][2] = CheckersState.BLACK_COMMON;
         board[3][4] = CheckersState.BLACK_COMMON;
         board[3][6] = CheckersState.BLACK_COMMON;
         board[4][5] = CheckersState.BLACK_COMMON;
         board[4][3] = CheckersState.WHITE_COMMON;
         board[4][7] = CheckersState.WHITE_COMMON;
         board[5][2] = CheckersState.BLANK_POSITION;
         board[5][6] = CheckersState.BLANK_POSITION;
         board[6][1] = CheckersState.BLANK_POSITION;
         board[7][4] = CheckersState.BLANK_POSITION;
         board[4][1] = CheckersState.BLACK_COMMON;*/



        currentState.setBoard(board);
        currentState.refreshPiecesAmount();
    }

    public CheckersState getCurrentState() {
        return currentState;
    }

    public List<CheckersMovement> availableMovements() {
        return avaiableMovements(currentState);
    }

    public static List<CheckersMovement> avaiableMovements(CheckersState state) {
        byte[][] board = state.getBoard();

        List<CheckersMovement> list = new ArrayList<CheckersMovement>();
        List<CheckersMovement> nearJumps;
        short maxJumps = 0;

        for (int i = 0; i < 8; i++) {
            for (int j = (i % 2 == 0) ? 1 : 0; j < 8; j += 2) {
                switch (state.getCurrentPlayer()) {
                    case CheckersState.WHITE_PLAYER:
                        switch (board[i][j]) {

                            case CheckersState.WHITE_COMMON:

                                if (maxJumps == 0) {
                                    List<CheckersMovement> nearMovements = getNearMovements(state, CheckersState.WHITE_COMMON, i, j);
                                    if (nearMovements != null && nearMovements.size() > 0) {
                                        for (CheckersMovement cm : nearMovements) {
                                            //check become king
                                            CheckersOperation lastPos = cm.getLast(CheckersOperation.TARGET_POSITION);
                                            if (lastPos.x == 0) {
                                                cm.getMovementsList().add(new CheckersOperation(lastPos.x, lastPos.y, CheckersOperation.BECOME_KING));
                                            }
                                            list.add(cm);
                                        }
                                    }
                                }
                                nearJumps = getNearJumps(state, CheckersState.WHITE_COMMON, i, j, null);
                                if (nearJumps != null && nearJumps.size() > 0) {
                                    for (CheckersMovement cm : nearJumps) {
                                        if (maxJumps < cm.countOperations(CheckersOperation.REMOVE_PIECE)) {
                                            maxJumps = cm.countOperations(CheckersOperation.REMOVE_PIECE);
                                            if (list.size() > 0) {
                                                list = new ArrayList<CheckersMovement>();
                                            }
                                        }
                                        if (cm.countOperations(CheckersOperation.REMOVE_PIECE) >= maxJumps) {
                                            //check become king
                                            CheckersOperation lastPos = cm.getLast(CheckersOperation.TARGET_POSITION);
                                            if (lastPos.x == 0) {
                                                cm.getMovementsList().add(new CheckersOperation(lastPos.x, lastPos.y, CheckersOperation.BECOME_KING));
                                            }
                                            list.add(cm);
                                        }
                                    }
                                }
                                break;

                            case CheckersState.WHITE_KING:
                                if (maxJumps == 0) {
                                    List<CheckersMovement> nearMovements = getNearMovements(state, CheckersState.WHITE_KING, i, j);
                                    if (nearMovements != null && nearMovements.size() > 0) {
                                        list.addAll(nearMovements);
                                    }
                                }
                                nearJumps = getNearJumps(state, CheckersState.WHITE_KING, i, j, null);
                                if (nearJumps != null && nearJumps.size() > 0) {
                                    for (CheckersMovement cm : nearJumps) {
                                        if (maxJumps < cm.countOperations(CheckersOperation.REMOVE_PIECE)) {
                                            maxJumps = cm.countOperations(CheckersOperation.REMOVE_PIECE);
                                            if (list.size() > 0) {
                                                list = new ArrayList<CheckersMovement>();
                                            }
                                        }
                                        if (cm.countOperations(CheckersOperation.REMOVE_PIECE) >= maxJumps) {
                                            list.add(cm);
                                        }
                                    }
                                }
                                break;
                        }
                        break;
                    case CheckersState.BLACK_PLAYER:
                        switch (board[i][j]) {
                            case CheckersState.BLACK_COMMON:
                                if (maxJumps == 0) {
                                    List<CheckersMovement> nearMovements = getNearMovements(state, CheckersState.BLACK_COMMON, i, j);
                                    if (nearMovements != null && nearMovements.size() > 0) {
                                        for (CheckersMovement cm : nearMovements) {
                                            //check become king
                                            CheckersOperation lastPos = cm.getLast(CheckersOperation.TARGET_POSITION);
                                            if (lastPos.x == 7) {
                                                cm.getMovementsList().add(new CheckersOperation(lastPos.x, lastPos.y, CheckersOperation.BECOME_KING));
                                            }
                                            list.add(cm);
                                        }
                                    }
                                }
                                nearJumps = getNearJumps(state, CheckersState.BLACK_COMMON, i, j, null);
                                if (nearJumps != null && nearJumps.size() > 0) {
                                    for (CheckersMovement cm : nearJumps) {
                                        if (maxJumps < cm.countOperations(CheckersOperation.REMOVE_PIECE)) {
                                            maxJumps = cm.countOperations(CheckersOperation.REMOVE_PIECE);
                                            if (list.size() > 0) {
                                                list = new ArrayList<CheckersMovement>();
                                            }
                                        }
                                        if (cm.countOperations(CheckersOperation.REMOVE_PIECE) >= maxJumps) {
                                            //check become king
                                            CheckersOperation lastPos = cm.getLast(CheckersOperation.TARGET_POSITION);
                                            if (lastPos.x == 7) {
                                                cm.getMovementsList().add(new CheckersOperation(lastPos.x, lastPos.y, CheckersOperation.BECOME_KING));
                                            }
                                            list.add(cm);
                                        }
                                    }
                                }
                                break;

                            case CheckersState.BLACK_KING:

                                if (maxJumps == 0) {
                                    List<CheckersMovement> nearMovements = getNearMovements(state, CheckersState.BLACK_KING, i, j);
                                    if (nearMovements != null && nearMovements.size() > 0) {
                                        list.addAll(nearMovements);
                                    }
                                }
                                nearJumps = getNearJumps(state, CheckersState.BLACK_KING, i, j, null);
                                if (nearJumps != null && nearJumps.size() > 0) {
                                    for (CheckersMovement cm : nearJumps) {
                                        if (maxJumps < cm.countOperations(CheckersOperation.REMOVE_PIECE)) {
                                            maxJumps = cm.countOperations(CheckersOperation.REMOVE_PIECE);
                                            if (list.size() > 0) {
                                                list = new ArrayList<CheckersMovement>();
                                            }
                                        }
                                        if (cm.countOperations(CheckersOperation.REMOVE_PIECE) >= maxJumps) {
                                            list.add(cm);
                                        }
                                    }
                                }
                                break;
                        }
                        break;
                }
            }
        }

        return list;
    }

    private static List<CheckersMovement> getNearMovements(CheckersState state, byte pieceType, int i, int j) {
        byte[][] board = state.getBoard();

        List<CheckersMovement> list = new ArrayList<CheckersMovement>();
        List<CheckersOperation> operations;

        //upper left
        if (i >= 1 && j >= 1 && board[i - 1][j - 1] == CheckersState.BLANK_POSITION
                && (pieceType == CheckersState.WHITE_COMMON
                || pieceType == CheckersState.WHITE_KING
                || pieceType == CheckersState.BLACK_KING)) {
            operations = new ArrayList<CheckersOperation>();
            operations.add(new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION));
            operations.add(new CheckersOperation(i - 1, j - 1, CheckersOperation.TARGET_POSITION));
            list.add(new CheckersMovement(operations));
        }

        //upper right
        if (i >= 1 && j <= 6 && board[i - 1][j + 1] == CheckersState.BLANK_POSITION
                && (pieceType == CheckersState.WHITE_COMMON
                || pieceType == CheckersState.WHITE_KING
                || pieceType == CheckersState.BLACK_KING)) {
            operations = new ArrayList<CheckersOperation>();
            operations.add(new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION));
            operations.add(new CheckersOperation(i - 1, j + 1, CheckersOperation.TARGET_POSITION));
            list.add(new CheckersMovement(operations));
        }

        //lower left
        if (i <= 6 && j >= 1 && board[i + 1][j - 1] == CheckersState.BLANK_POSITION
                && (pieceType == CheckersState.BLACK_COMMON
                || pieceType == CheckersState.WHITE_KING
                || pieceType == CheckersState.BLACK_KING)) {
            operations = new ArrayList<CheckersOperation>();
            operations.add(new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION));
            operations.add(new CheckersOperation(i + 1, j - 1, CheckersOperation.TARGET_POSITION));
            list.add(new CheckersMovement(operations));
        }

        //lower right
        if (i <= 6 && j <= 6 && board[i + 1][j + 1] == CheckersState.BLANK_POSITION
                && (pieceType == CheckersState.BLACK_COMMON
                || pieceType == CheckersState.WHITE_KING
                || pieceType == CheckersState.BLACK_KING)) {
            operations = new ArrayList<CheckersOperation>();
            operations.add(new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION));
            operations.add(new CheckersOperation(i + 1, j + 1, CheckersOperation.TARGET_POSITION));
            list.add(new CheckersMovement(operations));
        }

        if (list.size() > 0) {
            return list;
        } else {
            return null;
        }
    }

    private static List<CheckersMovement> getNearJumps(CheckersState state, byte pieceType, int i, int j, CheckersMovement previousJumps) {
        byte[][] board = state.getBoard();

        List<CheckersMovement> list = new ArrayList<CheckersMovement>();
        List<CheckersOperation> operations;

        //upper left
        if (i >= 2 && j >= 2
                && (board[i - 2][j - 2] == CheckersState.BLANK_POSITION || (previousJumps == null ? false : previousJumps.containsAsSourceOrTarget(i - 2, j - 2)))
                && (previousJumps == null ? true : !previousJumps.containsAsRemove(i - 1, j - 1))
                && (board[i - 1][j - 1] == ((pieceType == CheckersState.WHITE_COMMON || pieceType == CheckersState.WHITE_KING) ? CheckersState.BLACK_COMMON : CheckersState.WHITE_COMMON)
                || board[i - 1][j - 1] == ((pieceType == CheckersState.WHITE_COMMON || pieceType == CheckersState.WHITE_KING) ? CheckersState.BLACK_KING : CheckersState.WHITE_KING))
                && (pieceType == CheckersState.WHITE_COMMON
                || pieceType == CheckersState.BLACK_COMMON
                || pieceType == CheckersState.WHITE_KING
                || pieceType == CheckersState.BLACK_KING)) {
            operations = new ArrayList<CheckersOperation>();
            operations.add(new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION));
            operations.add(new CheckersOperation(i - 2, j - 2, CheckersOperation.TARGET_POSITION));
            operations.add(new CheckersOperation(i - 1, j - 1, CheckersOperation.REMOVE_PIECE));
            list.add(new CheckersMovement(operations));
        }

        //upper right
        if (i >= 2 && j <= 5
                && (board[i - 2][j + 2] == CheckersState.BLANK_POSITION || (previousJumps == null ? false : previousJumps.containsAsSourceOrTarget(i - 2, j + 2)))
                && (previousJumps == null ? true : !previousJumps.containsAsRemove(i - 1, j + 1))
                && (board[i - 1][j + 1] == ((pieceType == CheckersState.WHITE_COMMON || pieceType == CheckersState.WHITE_KING) ? CheckersState.BLACK_COMMON : CheckersState.WHITE_COMMON)
                || board[i - 1][j + 1] == ((pieceType == CheckersState.WHITE_COMMON || pieceType == CheckersState.WHITE_KING) ? CheckersState.BLACK_KING : CheckersState.WHITE_KING))
                && (pieceType == CheckersState.WHITE_COMMON
                || pieceType == CheckersState.BLACK_COMMON
                || pieceType == CheckersState.WHITE_KING
                || pieceType == CheckersState.BLACK_KING)) {
            operations = new ArrayList<CheckersOperation>();
            operations.add(new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION));
            operations.add(new CheckersOperation(i - 2, j + 2, CheckersOperation.TARGET_POSITION));
            operations.add(new CheckersOperation(i - 1, j + 1, CheckersOperation.REMOVE_PIECE));
            list.add(new CheckersMovement(operations));
        }

        //lower left
        if (i <= 5 && j >= 2
                && (board[i + 2][j - 2] == CheckersState.BLANK_POSITION || (previousJumps == null ? false : previousJumps.containsAsSourceOrTarget(i + 2, j - 2)))
                && (previousJumps == null ? true : !previousJumps.containsAsRemove(i + 1, j - 1))
                && (board[i + 1][j - 1] == ((pieceType == CheckersState.WHITE_COMMON || pieceType == CheckersState.WHITE_KING) ? CheckersState.BLACK_COMMON : CheckersState.WHITE_COMMON)
                || board[i + 1][j - 1] == ((pieceType == CheckersState.WHITE_COMMON || pieceType == CheckersState.WHITE_KING) ? CheckersState.BLACK_KING : CheckersState.WHITE_KING))
                && (pieceType == CheckersState.WHITE_COMMON
                || pieceType == CheckersState.BLACK_COMMON
                || pieceType == CheckersState.WHITE_KING
                || pieceType == CheckersState.BLACK_KING)) {
            operations = new ArrayList<CheckersOperation>();
            operations.add(new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION));
            operations.add(new CheckersOperation(i + 2, j - 2, CheckersOperation.TARGET_POSITION));
            operations.add(new CheckersOperation(i + 1, j - 1, CheckersOperation.REMOVE_PIECE));
            list.add(new CheckersMovement(operations));
        }

        //lower right
        if (i <= 5 && j <= 5
                && (board[i + 2][j + 2] == CheckersState.BLANK_POSITION || (previousJumps == null ? false : previousJumps.containsAsSourceOrTarget(i + 2, j + 2)))
                && (previousJumps == null ? true : !previousJumps.containsAsRemove(i + 1, j + 1))
                && (board[i + 1][j + 1] == ((pieceType == CheckersState.WHITE_COMMON || pieceType == CheckersState.WHITE_KING) ? CheckersState.BLACK_COMMON : CheckersState.WHITE_COMMON)
                || board[i + 1][j + 1] == ((pieceType == CheckersState.WHITE_COMMON || pieceType == CheckersState.WHITE_KING) ? CheckersState.BLACK_KING : CheckersState.WHITE_KING))
                && (pieceType == CheckersState.WHITE_COMMON
                || pieceType == CheckersState.BLACK_COMMON
                || pieceType == CheckersState.WHITE_KING
                || pieceType == CheckersState.BLACK_KING)) {
            operations = new ArrayList<CheckersOperation>();
            operations.add(new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION));
            operations.add(new CheckersOperation(i + 2, j + 2, CheckersOperation.TARGET_POSITION));
            operations.add(new CheckersOperation(i + 1, j + 1, CheckersOperation.REMOVE_PIECE));
            list.add(new CheckersMovement(operations));
        }

        if (list.isEmpty()) {
            if (previousJumps != null) {
                ArrayList<CheckersMovement> arrayList = new ArrayList<CheckersMovement>();
                arrayList.add(previousJumps);
                return arrayList;
            } else {
                return null;
            }
        } else {
            ArrayList<CheckersMovement> finalMovements = new ArrayList<CheckersMovement>();
            for (CheckersMovement cm : list) {
                if (previousJumps != null) {
                    CheckersMovement clone = previousJumps.clone();
                    clone.getMovementsList().add(cm.getMovementsList().get(cm.getMovementsList().size() - 2));
                    clone.getMovementsList().add(cm.getMovementsList().get(cm.getMovementsList().size() - 1));
                    cm.setMovementsList(clone.getMovementsList());
                }
                List<CheckersMovement> nearJumps = getNearJumps(state, pieceType, cm.getLast(CheckersOperation.TARGET_POSITION).x, cm.getLast(CheckersOperation.TARGET_POSITION).y, cm);
                if (nearJumps != null) {
                    finalMovements.addAll(nearJumps);
                }
            }
            return finalMovements;
        }

    }
}
